'''

code-dialog 是 qsont 开源社区的代码社交平台

社交平台的设计应当保持简洁且面向移动端

讨论的中心应该是代码和项目

'''
from qsont.hash import sha256_hash
from flask import Flask, request, session, jsonify, render_template, redirect
from flask_sqlalchemy import SQLAlchemy
from flask_cors import CORS
from flask_jwt_extended import (
    JWTManager, create_access_token,
    create_refresh_token, jwt_required,
    get_jwt_identity, set_access_cookies,
    set_refresh_cookies, unset_jwt_cookies
)
import os
import jwt
from functools import wraps
from datetime import datetime, timedelta
import time
import pytz
import uuid

basedir = os.path.abspath(os.path.dirname(__file__)) # 脚本当前路径

UPLOAD_FOLDER = os.path.join(basedir, 'uploads') # 上传文件路径
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'git'} # 允许的扩展名
MAX_FILE_SIZE = 10* 1024 * 1024 # 最大文件限制 10M
os.makedirs(UPLOAD_FOLDER, exist_ok=True) # 确保目录存在

# app = Flask(__name__, static_folder='static', static_url_path='/static')
app = Flask(__name__, static_folder='static', static_url_path='/')
app.secret_key = os.urandom(24) # 用于对session数据进行签名，前后端分离场景大可不必
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'comm.db') # 设置sqlite数据库的位置
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # 关闭对对象修改的追踪
app.config['UPLOAD_FOLDER'] =  UPLOAD_FOLDER # 设置上传文件路径
app.config['MAX_CONTENT_LENGTH'] = MAX_FILE_SIZE # 设置最大文件大小
#app.config['SECRET_KEY'] = 'sdjFLskH1Shh4k4hl2SH3' # JWT密钥
app.config['JWT_SECRET_KEY'] = "S(Ujg9sfJS(SSG0sg23"
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(minutes=15) # 访问令牌有效期
app.config['JWT_REFRESH_TOKEN_EXPIRES'] = timedelta(days=30) # 刷新令牌有效期
app.config['JWT_TOKEN_LOCATION'] = ['headers', 'cookies'] # 允许从头部和cookie获取

allowed_origins = ['*']
CORS(app,
    origins=allowed_origins,
    supports_credentials=True,
    methods=['POST', 'OPTIONS'],
    allowed_headers=['Content-Type', 'Authorization']
) # 允许跨域访问
jwt = JWTManager(app) # jwt
db = SQLAlchemy(app) # 创建数据库类型，在这里应该只是获取它的配置

class User(db.Model):
    '''用户表类型
    具有主键编号、用户名、密码哈希、是否为管理员'''
    __tablename__ = 'users'
    __table_args__ = {
        'comment': '用户表',
        #'mysql_engine': 'InnoDB',
        #'mysql_charset': 'utf8mb4',
        'sqlite_autoincrement': False # sqlite 特有自增选项
    }

    id = db.Column(db.String(32), primary_key=True, comment='用户ID')
    email = db.Column(db.String(80), unique=True, nullable=False, comment='邮箱')
    username = db.Column(db.String(32), nullable=False, index=True, comment='用户名')
    password = db.Column(db.String(64), nullable=False, comment='密码。加密存储sha256')
    created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow, comment='创建时间')
    updated_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow,
                           onupdate=datetime.utcnow, comment='更新时间')
    is_deleted = db.Column(db.Boolean, nullable=False, default=False, comment='是否删除，0：未删除，1：已删除')
    isAdmin =db.Column(db.Boolean, default=False)
    posts = db.relationship('Post', backref='author', lazy='dynamic')
    comments = db.relationship('Comment', backref='author', lazy='dynamic')

    def __repr__(self):
        return f'<User {self.username}>'

class Post(db.Model):
    __tablename__ = 'posts'

    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(255), nullable=False)
    body = db.Column(db.Text, nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    comments = db.relationship('Comment', backref='post', lazy='dynamic')

    def __repr__(self):
        return f'<Post {self.title}>'

class Comment(db.Model):
    __tablename__ = 'comments'

    id = db.Column(db.Integer, primary_key=True)
    body = db.Column(db.Text, nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    updated_at = db.Column(db.DateTime, default=datetime.utcnow)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
    post_id = db.Column(db.Integer, db.ForeignKey('posts.id'), nullable=False)
    parent_id = db.Column(db.Integer, db.ForeignKey('comments.id'), nullable=True);
    children = db.relationship('Comment', backref=db.backref('parent', remote_side=[id]), lazy='dynamic')

    def __repr(self):
        return f'<Comment {self.id} by User {self.user_id}>'

'''创建数据库'''
with app.app_context():
    db.create_all()
######################
@app.before_request
def check_file_size():
    '''文件大小验证'''
    if request.method == 'POST':
        if request.content_length > app.config['MAX_CONTENT_LENGTH']:
            return jsonify({'error': 'File too large'}), 413

######################
@app.route('/api/v1/login', methods=['POST'])
def login():
    data = request.get_json()
    if data is None:
        return jsonify({'error': 'Invalid or missing JSON'}), 400
    
    email = data['username']
    password = sha256_hash(data['password'])

    # 在数据库中查找用户
    user = User.query.filter_by(email=email, password=password).first()
    if user is None:
        return jsonify({'error': 'Access denit'}), 401

    return jsonify({
        "message": 'success',
        'token': create_access_token(
            identity=email,
            additional_claims={'isAdmin': user.isAdmin}
        )
    }), 200

######################
@app.route('/api/v1/regiest', methods=['POST'])
def regiest():
    data = request.get_json()
    if data is None:
        return jsonify({'error': 'Invalid or missing JSON'}), 400
    
    email = data['username']
    password = sha256_hash(data['password'])

    # 使用 uuid5 （基于命名空间和名称生成确定性UUID
    namespace = uuid.NAMESPACE_OID # 可以使用任何预定义的命名空间
    unique_id = uuid.uuid5(namespace, f'{email}:{password}') # 128位，32个十六进制字符表示

    # 查找旧的用户
    user = User.query.filter_by(email=email).first()
    if user is not None:
        return jsonify({'error': 'This username is already used'}), 409

    # 创建新用户
    new_user = User(id=unique_id.hex, email=email, username=unique_id.hex, password=password)
    db.session.add(new_user)
    db.session.commit()

    return jsonify({
        'message': "success",
        'token': create_access_token(
            identity=email,
            additional_claims={"isAdmin": new_user.isAdmin}
        )
    }), 200

######################
@app.route('/api/v1/submit-post', methods=['POST'])
@jwt_required() # 需要有效的JWT
def submit_post():
    current_user = get_jwt_identity()
    data = request.get_json()
    if data is None:
        return jsonify({'error': 'Invalid or missing JSON'}), 400

    title = data['title']
    content= data['content']

    # 查找旧的用户
    user = User.query.filter_by(email=current_user).first()
    if user is None:
        return jsonify({'error': 'This username is not exist'}), 404

    # 创建 Post 实例
    new_post = Post(
        title=title,
        body=content,
        user_id=user.id
    )

    # 添加到数据库会话
    db.session.add(new_post)

    # 提交保存到数据库
    db.session.commit()

    return jsonify({
        'message': 'success',
        'new_post_id': new_post.id
    }), 200

######################
def trans_time(utc_time) -> str:
    beijing_tz = pytz.timezone('Asia/Shanghai')
    beijing_time = utc_time.replace(tzinfo=pytz.utc).astimezone(beijing_tz)
    return beijing_time.strftime('%Y-%m-%d %H:%M:%S')

def get_user_name(id) -> str:
    user = User.query.filter_by(id=id).first()
    if user is None:
        return ''
    else:
        return user.email

def get_by_id(limit):
    posts = Post.query.order_by(Post.id.asc()).limit(limit).all()
    return [{
        'title': post.title,
        'body': post.body,
        'datetime': trans_time(post.updated_at),
        'author': get_user_name(post.user_id)
    } for post in posts]
    
@app.route('/api/v1/show_posts', methods=["GET"])
@jwt_required()
def show_posts():
    limit = request.args.get('limit', type=int)
    posts_data = get_by_id(limit)

    return jsonify({
        'message': 'success',
        'posts': posts_data
    })

######################
if __name__ == '__main__':
    crt = os.path.join(basedir, '../crt/qsont.xyz.crt') 
    key = os.path.join(basedir, '../crt/qsont.xyz.key') 
    app.run(
        host="0.0.0.0",
        port=8016,
        #ssl_context=(crt, key),
        debug=True
    )
